`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2020/09/14 23:25:53
// Design Name: 
// Module Name: Carya
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module Carya(
    input   wire        clk,
    input   wire        rst,
    
    input   wire [31:0] i_data,
    input   wire [31:0] i_inst,
    
    output  wire [31:0] o_data_addr,
    output  wire [31:0] o_wr_data,
    output  wire        o_data_we,
    
    output  wire [31:0] o_inst_addr,
    output  wire        o_inst_en
    );
    
    wire if_is_jump;
    wire [31:0] if_jump_addr;
    wire [31:0] if_inst_addr;
    wire if_pause;
    IF instruction_fetch(
        .clk(clk),
        .rst(rst),
        .pause(if_pause),
        
        .i_is_jump(if_is_jump),
        .i_new_inst_addr(if_jump_addr),
        
        .o_inst_addr(if_inst_addr)
    );
    
    // assign output port instruction address
    assign o_inst_addr = if_inst_addr;
    
    // latch
    wire [31:0] latch_inst_addr;
    wire latch_pause;
    Latch32 latch0(
        .clk(clk),
        .rst(rst),
        .pause(latch_pause),
        
        .in(if_inst_addr),
        .out(latch_inst_addr)
    );
    
    
    // if_id
    wire [31:0] if_id_inst;
    wire [31:0] if_id_inst_addr;
    wire if_id_pause;
    IF_ID if_id(
        .rst(rst),
        .clk(clk),
        .pause(if_id_pause),
        
        .i_inst(i_inst),
        .i_inst_addr(latch_inst_addr),
        
        .o_inst(if_id_inst),
        .o_inst_addr(if_id_inst_addr)
    );
    
    wire [4:0] reg_read_rs;
    wire [4:0] reg_read_rt;
    wire [4:0] reg_read_rd;
    wire reg_read_we;
    wire [31:0] reg_read_wr_data;
    wire [4:0] reg_read_side_addr_1;
    wire [4:0] reg_read_side_addr_2;
    wire [31:0] reg_read_side_data_1;
    wire [31:0] reg_read_side_data_2;
    wire reg_read_side_en_1;
    wire reg_read_side_en_2;
    wire [31:0] reg_read_rs_out;
    wire [31:0] reg_read_rt_out;
    RegRead reg_read(
        .clk(clk),
        .rst(rst),
        
        .i_rs(reg_read_rs),
        .i_rt(reg_read_rt),
        .i_rd(reg_read_rd),
        .i_we(reg_read_we),
        .i_wr_data(reg_read_wr_data),
        
        .i_side_addr_1(reg_read_side_addr_1),
        .i_side_addr_2(reg_read_side_addr_2),
        .i_side_data_1(reg_read_side_data_1),
        .i_side_data_2(reg_read_side_data_2),
        .i_side_en_1(reg_read_side_en_1),
        .i_side_en_2(reg_read_side_en_2),
        
        .o_rs_out(reg_read_rs_out),
        .o_rt_out(reg_read_rt_out)
    );
    
    
    wire [5:0] id_op;
    wire [4:0] id_rs;
    wire [4:0] id_rt;
    wire [4:0] id_rd;
    wire [4:0] id_sa;
    wire [5:0] id_funct;
    ID instruction_decoder(
        .i_inst(if_id_inst),
        .i_rs_out(reg_read_rs_out),
        .i_rt_out(reg_read_rt_out),
        
        .o_op(id_op),
        .o_rs(id_rs),
        .o_rt(id_rt),
        .o_rd(id_rd),
        .o_sa(id_sa),
        .o_funct(id_funct)
    );
    
    
    // connect ID and RegRead
    assign reg_read_rs = id_rs;
    assign reg_read_rt = id_rt;
    
    wire jump_is_jump;
    wire [31:0] jump_jump_addr;
    Jump jump(
        .i_pc(if_id_inst_addr),
        .i_inst(if_id_inst),
        .i_rs_out(reg_read_rs_out),
        .i_rt_out(reg_read_rt_out),
        
        .o_is_jump(jump_is_jump),
        .o_jump_addr(jump_jump_addr)
    );
    
    // connect Jump and IF
    assign if_is_jump = jump_is_jump;
    assign if_jump_addr = jump_jump_addr;
    
    
    // ID_EX
    wire [31:0] id_ex_rs_out;
    wire [31:0] id_ex_rt_out;
    wire [31:0] id_ex_inst_addr;
    wire [31:0] id_ex_inst;
    wire [31:0] id_ex_imm32_zero;
    wire [31:0] id_ex_imm32_sign;
    wire id_ex_nop;
    ID_EX id_ex(
        .clk(clk),
        .rst(rst),
        .nop(id_ex_nop),
        
        .i_rs_out(reg_read_rs_out),
        .i_rt_out(reg_read_rt_out),
        .i_inst_addr(if_id_inst_addr),
        .i_inst(if_id_inst),
        
        .o_rs_out(id_ex_rs_out),
        .o_rt_out(id_ex_rt_out),
        .o_inst_addr(id_ex_inst_addr),
        .o_inst(id_ex_inst),
        .o_imm32_zero(id_ex_imm32_zero),
        .o_imm32_sign(id_ex_imm32_sign)
    );
    
    
    // EX
    wire [31:0] ex_dm_addr;
    wire ex_dm_we;
    wire [4:0] ex_wb_addr;
    wire ex_wb_en;
    wire ex_is_mem;
    wire [31:0] ex_wr_data;
    EX execution(
        .i_rs_out(id_ex_rs_out),
        .i_rt_out(id_ex_rt_out),
        .i_inst_addr(id_ex_inst_addr),
        .i_inst(id_ex_inst),
        .i_imm32_zero(id_ex_imm32_zero),
        .i_imm32_sign(id_ex_imm32_sign),
        
        .o_dm_addr(ex_dm_addr),
        .o_dm_we(ex_dm_we),
        .o_wb_addr(ex_wb_addr),
        .o_wb_en(ex_wb_en),
        .o_is_mem(ex_is_mem),
        .o_wr_data(ex_wr_data)
    );
    
    // connect EX and DM
    assign o_data_addr = ex_dm_addr;
    assign o_wr_data = ex_wr_data;
    assign o_data_we = ex_dm_we;
    
    // connect EX output to RegRead
    assign reg_read_side_addr_1 = ex_wb_addr;
    assign reg_read_side_data_1 = ex_wr_data;
    assign reg_read_side_en_1 = ex_wb_en;
    
    // load delay
    wire ld_delay;
    wire ld_ndelay;
    LD ld(
        .i_inst(id_ex_inst),
        .i_rs(id_rs),
        .i_rt(id_rt),
        
        .o_delay(ld_delay),
        .o_ndelay(ld_ndelay)
    );
    assign if_pause = ld_delay;
    assign o_inst_en = ld_ndelay;
    assign latch_pause = ld_delay;
    assign if_id_pause = ld_delay;
    assign id_ex_nop = ld_delay;
    
    
    // ex_mem
    wire [4:0] ex_mem_wb_addr;
    wire ex_mem_wb_en;
    wire [31:0] ex_mem_wb_data;
    wire ex_mem_is_mem;
    EX_Mem ex_mem(
        .clk(clk),
        .rst(rst),
        
        .i_wb_addr(ex_wb_addr),
        .i_wb_en(ex_wb_en),
        .i_wb_data(ex_wr_data),
        .i_is_mem(ex_is_mem),
        
        .o_wb_addr(ex_mem_wb_addr),
        .o_wb_en(ex_mem_wb_en),
        .o_wb_data(ex_mem_wb_data),
        .o_is_mem(ex_mem_is_mem)
    );
    
    
    wire [31:0] mem_wb_data;
    wire mem_wb_en;
    wire [4:0] mem_wb_addr;
    Mem memory(
        .i_is_mem(ex_mem_is_mem),
        .i_wb_addr(ex_mem_wb_addr),
        .i_wb_data(ex_mem_wb_data),
        .i_wb_en(ex_mem_wb_en),
        .i_mem_data(i_data),
        
        .o_wb_data(mem_wb_data),
        .o_wb_en(mem_wb_en),
        .o_wb_addr(mem_wb_addr)
    );
    
    
    // connect Mem output to RegRead
    assign reg_read_side_addr_2 = mem_wb_addr;
    assign reg_read_side_data_2 = mem_wb_data;
    assign reg_read_side_en_2 = mem_wb_en;
    assign reg_read_rd = mem_wb_addr;
    assign reg_read_we = mem_wb_en;
    assign reg_read_wr_data = mem_wb_data;
    
endmodule
